/*
	Copyright (C) 2009 Salil Bhagurkar
	
	This file is part of ViSE
	
	ViSE is free software: you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.
	
	ViSE is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU Lesser General Public License for more details.
	
	You should have received a copy of the GNU Lesser General Public License
	along with ViSE.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <shared.h>

struct boot {
	u8 jmp[3];
	u8 oemid[8];
	u16 bytes_per_sect;
	u8 sect_per_clus;
	u16 resvd_sect;
	u8 fats;
	u16 root_dirent;
	u16 total_sect;
	u8 media;
	u16 sect_per_fat;
	u16 sect_per_track;
	u16 heads;
	u32 hidden_sect;
	u32 total_sect_large;
}__attribute__((packed));

struct boot_fat12 {
	u8 drive;
	u8 flags_nt;
	u8 sign;
	u32 vol_id;
	u8 vol_lab[11];
	u8 sys_id[8];
}__attribute__((packed));

struct boot_fat16 {
	u8 drive;
	u8 flags_nt;
	u8 sign;
	u32 vol_id;
	u8 vol_lab[11];
	u8 sys_id[8];
}__attribute__((packed));

struct boot_fat32 {
	u32 sect_per_fat;
	u16 flags;
	u16 fs_ver;
	u32 root_clus;
	u16 fsinfo_clus;
	u16 boot_backup_clus;
	u8 resvd[12];
	u8 drive;
	u8 flags_nt;
	u8 sign2;
	u32 vol_id;
	u8 vol_lab[11];
	u8 sys_id[8];
}__attribute__((packed));


struct dirent {
	u8 name[11];
	u8 attr;
	u8 resvd_nt;
	u8 crt_time_tenth;
	u16 crt_time;
	u16 crt_date;
	u16 last_acc_date;
	u16 first_clus_high;
	u16 write_time;
	u16 write_date;
	u16 first_clus_low;
	u32 file_size;
}__attribute__((packed));

#define ATTR_READ_ONLY 1
#define ATTR_HIDDEN 2
#define ATTR_SYSTEM 4
#define ATTR_VOLUME_ID 8
#define ATTR_DIRECTORY 0x10
#define ATTR_ARCHIVE 0x20

#define ATTR_LONG_NAME_MASK (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)


/*How this code works:
1.Driver is registered
2.Upon initiation the driver registers a notifier to listen to appearing storage devices
3.For each storage device, it registers a notifier to listen to appearing volumes
4.For each volume, it performs detection of FAT and its type. If detected, it registers an enumerator
	that will enumerate files/dirs in the dir (here, root dir). This enumerator has its class
	as the 'fat_info' structure that caches all the fs info
5.Upon enumeration of files/dir in the root directory, each file is plugged in as a node_fs
	with the class as 'fat_file_info' structure that holds the directory entry as is in
	the FAT, the volume device pointer, and the pointer to the FAT enumerator above which
	can be used to get the fat_info structure. Each file has an in-build enumerator allowing to
	enumerate its contents. (if its a directory)
*/


/*Induction:

Offset = 6000
Count = 7000
file_size = 20000

get start cluster of file

starting cluster = 1 (6000/4096)
Move to this cluster getting address of the cluster 1 by looking up cluster 0
Now we know the location of cluster 1 relative to the start of file.
Cluster 0 let it be 12 and cluster 1 = 13
Now figure out how many clusters need to be read
7000/4096 = 1, add 1 to get more data
so including cluster 1 we need to read 2 clusters
So, starting cluster is 1 and number of clusters to read is 2
Read them all to a buffer
Now simply get the actual offset of the required data (6000 - (6000 / 4096) * 4096 = 1904)
And, we know the number of bytes = 7000.
memcpy!!!

Now above at some points we need to check if we are overflowing the file size.
*/



struct fat_info {
	u8 type;
	u8 buf[512];
	u32 fatsz, total_sect, root_sect, data_sect, first_data_sect;
	struct boot *boot;
	struct boot_fat12 *boot_fat12;
	struct boot_fat16 *boot_fat16;
	struct boot_fat32 *boot_fat32;
};


//This is the combined info structure containing info on operating on the volume and on the directory entry
//This is plugged into each file
struct fat_file_info {
	struct node_fs *dev; //This volume or device on which the fs is mounted
	struct node_fs *fat_enum; //FAT enumerator of this directory.
	struct dirent dirent; //Directory entry of this file
};

//All functions are passed with a pointer to the fat enumerator which contains the info about the fs on the parent volume


u32 get_first_sector_of_cluster(struct fat_info *fat_info, u32 clus)
{
	return ((clus - 2) * (fat_info->boot->sect_per_clus)) + fat_info->first_data_sect;
}

u32 get_eoc(struct fat_info *fat_info)
{
	switch(fat_info->type) {
		case 12: return 0x0fff;
		case 16: return 0xffff;
		case 32: return 0x0fffffff;
	}
}

u32 get_cluster_entry(struct fat_info *fat_info, struct node_fs *dev, u32 n) //n is the cluster for which we want the next entry
{
	u32 fat_sect, fat_offset, fat_ent_offset, fat_clus_ent_val;
	u8 buf[1024];
	//All from the not so fat FAT spec
	switch(fat_info->type) {
		case 12:
			fat_offset = n + (n / 2);
			break;
		case 16:
			fat_offset = n * 2;
			break;
		case 32:
			fat_offset = n * 4;
			break;
	}
	fat_sect = (fat_info->boot->resvd_sect) + (fat_offset / (fat_info->boot->bytes_per_sect));
	fat_ent_offset = fat_offset % (fat_info->boot->bytes_per_sect);
	dev->read(dev, buf, fat_sect, 2);
	switch(fat_info->type) {
		case 12:
			fat_clus_ent_val = *((u16 *) &buf[fat_ent_offset]);
			if(n & 1)
				fat_clus_ent_val = fat_clus_ent_val >> 4;
			else
				fat_clus_ent_val = fat_clus_ent_val & 0x0FFF;
			break;
		case 16:
			fat_clus_ent_val = *((u16 *) &buf[fat_ent_offset]);
			break;
		case 32:
			fat_clus_ent_val = (*((u32 *) &buf[fat_ent_offset])) & 0x0fffffff;
			break;
	}
	return fat_clus_ent_val;
}


/*Decode stupid 8.3 to sane names with 13 characters including a dot and a null*/
char *decode_fat_name(char *name_dest, char *name_src)
{
	u32 i = 0;
	while(name_src[i] != 0x20) {
		name_dest[i] = name_src[i];
		i++;
		if(i >= 8)
			break;
	}
	if(name_src[8] != 0x20) {
		name_dest[i++] = '.';
		name_dest[i++] = name_src[8];
		name_dest[i++] = name_src[9];
		name_dest[i++] = name_src[10];
		name_dest[i++] = 0;
	} else
		name_dest[i++] = 0;
	return name_dest;
}


/*This decodes the four most essential fields required to operate on the file or operate
on the file system.
fat_file_info	: Defined above, used to get the structures below
fat_info	: Information about the file system (imp constants from boot, boot_fatxx etc)
dev		: Device (either storage or volume) to perform read/writes to
dirent		: Directory entry of this file/directory which contains info about name, location etc
*/
void inline decode_fs_info(struct fat_file_info **fat_file_info, struct fat_info **fat_info,
	struct node_fs **dev, struct dirent **dirent, struct node_fs *this)
{
	*fat_file_info = (struct fat_file_info *)this->class; //Information about this file
	*fat_info = (*fat_file_info)->fat_enum->class; //Go to the fat_enumerator and then get fat_info
	*dev = (*fat_file_info)->dev;
	*dirent = &((*fat_file_info)->dirent);
}

int fat_file_read(struct node_fs *this, void *buf, u32 offset, u32 count);

int legal_name(struct dirent *test)
{
	u32 i;
	for(i=0;i<11;i++)	{
		if(test->name[i]<0x20 || test->name[i]==0x22 || test->name[i]==0x2a || test->name[i]==0x2b || test->name[i]==0x2c || test->name[i]==0x2e || test->name[i]==0x2f || test->name[i]==0x3a || test->name[i]==0x3b || test->name[i]==0x3c || test->name[i]==0x3d || test->name[i]==0x3e || test->name[i]==0x3f || test->name[i]==0x5b || test->name[i]==0x5c || test->name[i]==0x5d || test->name[i]==0x7c || test->name[i] == 0xe5)
			return 0;
	}
	return 1;
}

/*This function is called when a directory entry listed in the fs is enumerated.*/
int fat_file_enumerate(struct node_fs *this)
{
	struct fat_file_info *fat_file_info;
	struct fat_info *fat_info;
	struct node_fs *dev;
	struct dirent *dirent;
	char name[13];
	
	decode_fs_info(&fat_file_info, &fat_info, &dev, &dirent, this);
	
	if(!(dirent->attr & ATTR_DIRECTORY))
		return EPINV;
	
	decode_fat_name(name, dirent->name);
	
	printk("Enumerate %s\n", name);
	
	
	u32 clus, i = 0;
	u8 *buf;
	struct node_fs *plug;
	
	clus =  ((dirent->first_clus_high) << 16) | (dirent->first_clus_low);
		
		//Get one cluster
	buf = (u8 *)malloc((fat_info->boot->sect_per_clus * 512));
	dev->read(dev, buf, get_first_sector_of_cluster(fat_info, clus), fat_info->boot->sect_per_clus);
		
	dirent = (struct dirent *)buf;
		
	while(dirent->name[0] != 0) {
		struct fat_file_info *n;
		if(legal_name(dirent)) {
			if(((dirent->attr) & ATTR_LONG_NAME_MASK) != ATTR_LONG_NAME_MASK) {
				decode_fat_name(name, dirent->name);
				plug = fs_create(name);
				plug->type = TYPE_FS_BOTH;
				n = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
				memcpy(&n->dirent, dirent, sizeof(struct dirent));
				n->dev = dev;
				n->fat_enum = fat_file_info->fat_enum;
				plug->class = n;
				plug->enumerate = fat_file_enumerate;
				plug->read = fat_file_read;
				fs_noparse_link(this, plug);
			}
		}
		dirent++;
		i++;
		if(i > (fat_info->boot->sect_per_clus * 512 / 32)) { //Cluster is over, get more
			clus = get_cluster_entry(fat_info, dev, clus);
			dev->read(dev, buf, get_first_sector_of_cluster(fat_info, clus), fat_info->boot->sect_per_clus);
			//Re-initialize dirent & i
			dirent = (struct dirent *)buf;
			i = 0;
			//And now continue reading more entries..
		}
	}
	free(buf);
	
	
	return 0;
}



/*Called when a read is issued to a node_fs in the fat fs*/
int fat_file_read(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	u32 cluster_bytes;
	u32 start_clus, start_sect;
	u32 start_clus_relative, clus_to_read, i, actual_offset;
	u8 *buf2;
	char name[13];
	
	struct fat_file_info *fat_file_info;
	struct fat_info *fat_info;
	struct node_fs *dev;
	struct dirent *dirent;
	
	
	decode_fs_info(&fat_file_info, &fat_info, &dev, &dirent, this);
	
	//Sanity checks. Checking for file size could actually be substituted by transferring
	//the file size number of bytes anyway instead of giving an error
	if((offset + count) > dirent->file_size) {
		printk("File is smaller, ***hole!\n");
		return EPBOUND;
	}
	if(dirent->attr & ATTR_DIRECTORY) {
		printk("This is a directory\n");
		return EPINV;
	}
	
	decode_fat_name(name, dirent->name);
	printk("Reading file : %s(%u bytes)\n", name, dirent->file_size);
	
	//Decode more information	
	start_clus = dirent->first_clus_low | ((dirent->first_clus_high) << 16);
	
	
	//Decode the number of bytes in one cluster.
	cluster_bytes = fat_info->boot->sect_per_clus * 512;
	
	
	
	//Decode information passed by the user, convert it into cluster language
	start_clus_relative = offset / cluster_bytes;
	clus_to_read = count / cluster_bytes + 1;
	
	
	buf2 = (u8 *)malloc(clus_to_read * cluster_bytes);
	for(i=0; i < start_clus_relative; i++) {
		start_clus = get_cluster_entry(fat_info, dev, start_clus);
		if(start_clus == get_eoc(fat_info)) {
			printk("fatfs: Got EOC before reaching end of file!\n");
			free(buf2);
			return ELLERR;
		}
	}
	
	for(i=0; i<clus_to_read; i++) {
		dev->read(dev, buf2 + cluster_bytes * i, get_first_sector_of_cluster(fat_info, start_clus), fat_info->boot->sect_per_clus);
		start_clus = get_cluster_entry(fat_info, dev, start_clus);
		if(start_clus == get_eoc(fat_info)) {
			printk("fatfs: Got EOC before reaching end of file!\n");
			free(buf2);
			return ELLERR;
		}
	}
	
	//Find the number of bytes we need to skip in the first cluster
	actual_offset = offset - (offset / cluster_bytes) * cluster_bytes;
	
	//Memcpy into the buffer
	memcpy(buf, buf2 + actual_offset, count);
	
	free(buf2);
	
	return 0;
}


/*This function is called when a volume detected with fat is enumerated*/
/*This enumerates the root directory*/
int fatfs_enum_vol(struct node_fs *this)
{
	struct node_fs *dev;
	struct fat_info *fat_info;
	struct dirent *dirent;
	struct node_fs *plug;
	char name[13];
	u8 *buf;
	u32 first_root_sect;
	
	dev = this->parent;
	fat_info = (struct fat_info *)this->class;
	
	//The first root sector depends on the fat type and is more centralized in 32
	if(fat_info->type == 12 || fat_info->type == 16)
		first_root_sect = fat_info->boot->resvd_sect + (fat_info->boot->fats * fat_info->boot->sect_per_fat);
	else
		first_root_sect = get_first_sector_of_cluster(fat_info, fat_info->boot_fat32->root_clus);
	
	//Allocate one cluster to read the root directory in
	
	
	if(fat_info->type == 12 || fat_info->type == 16) {
		u32 i = 0;
		buf = (u8 *)malloc((fat_info->boot->root_dirent) * 32);
		dev->read(dev, buf, first_root_sect, (fat_info->boot->root_dirent) * 32 / 512);
		dirent = (struct dirent *)buf;
		while(dirent->name[0]!=0 && i < (fat_info->boot->root_dirent)) {
			struct fat_file_info *n;
			if(legal_name(dirent)) {
				if(((dirent->attr) & ATTR_LONG_NAME_MASK) != ATTR_LONG_NAME_MASK) {
					decode_fat_name(name, dirent->name);
					plug = fs_create(name);
					plug->type = TYPE_FS_BOTH;
					n = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
					memcpy(&n->dirent, dirent, sizeof(struct dirent));
					n->dev = dev;
					n->fat_enum = this;
					plug->class = n;
					plug->enumerate = fat_file_enumerate;
					plug->read = fat_file_read;
					fs_noparse_link(dev, plug);
				}
			}
			dirent++;
			i++;
		}
		free(buf);
	} else { //If the FAT is 32 or 256 he he
		u32 clus, i = 0;
		clus = fat_info->boot_fat32->root_clus;
		
		//Get one cluster
		buf = (u8 *)malloc((fat_info->boot->sect_per_clus * 512));
		dev->read(dev, buf, get_first_sector_of_cluster(fat_info, clus), fat_info->boot->sect_per_clus);
		
		dirent = (struct dirent *)buf;
		
		while(dirent->name[0] != 0) {
			struct fat_file_info *n;
			if(legal_name(dirent)) {
				if(((dirent->attr) & ATTR_LONG_NAME_MASK) != ATTR_LONG_NAME_MASK) {
					decode_fat_name(name, dirent->name);
					plug = fs_create(name);
					plug->type = TYPE_FS_BOTH;
					n = (struct fat_file_info *)malloc(sizeof(struct fat_file_info));
					memcpy(&n->dirent, dirent, sizeof(struct dirent));
					n->dev = dev;
					n->fat_enum = this;
					plug->class = n;
					plug->enumerate = fat_file_enumerate;
					plug->read = fat_file_read;
					fs_noparse_link(dev, plug);
				}
			}
			dirent++;
			i++;
			if(i > (fat_info->boot->sect_per_clus * 512 / 32)) { //Cluster is over, get more
				clus = get_cluster_entry(fat_info, dev, clus);
				dev->read(dev, buf, get_first_sector_of_cluster(fat_info, clus), fat_info->boot->sect_per_clus);
				//Re-initialize dirent & i
				dirent = (struct dirent *)buf;
				i = 0;
				//And now continue reading more entries..
			}
		}
		free(buf);
	}
	
	return 0;
}

/*This function detects the file system and registers enunerators*/
int fatfs_storage_volume_notify(struct node_fs *this, struct node_fs *dev)
{
	u8 buf[512];
	u32 root_sect;
	u32 fatsz, total_sect, data_sect, count_of_clus, first_data_sect;
	struct node_fs *fat_enum;
	struct boot boot;
	struct boot_fat12 boot_fat12;
	struct boot_fat16 boot_fat16;
	struct boot_fat32 boot_fat32;
	//Try to detect fat fs here...
	dev->read(dev, buf, 0, 1);
	memcpy(&boot, buf, sizeof(struct boot));
	memcpy(&boot_fat12, buf+36, sizeof(struct boot_fat12));
	memcpy(&boot_fat16, buf+36, sizeof(struct boot_fat16));
	memcpy(&boot_fat32, buf+36, sizeof(struct boot_fat32));
	if((boot_fat12.sys_id[0] == 'F' && boot_fat12.sys_id[1] == 'A' && boot_fat12.sys_id[2] == 'T') || (boot_fat32.sys_id[0] == 'F' && boot_fat32.sys_id[1] == 'A' && boot_fat32.sys_id[2] == 'T')) {
		root_sect = ((boot.root_dirent * 32) + (boot.bytes_per_sect - 1)) / (boot.bytes_per_sect);
		if(boot.sect_per_fat != 0) {
			fatsz = boot.sect_per_fat;
		} else {
			fatsz = boot_fat32.sect_per_fat;
		}
		if(boot.total_sect != 0) {
			total_sect = boot.total_sect;
		} else {
			total_sect = boot.total_sect_large;
		}
		first_data_sect = (boot.resvd_sect + (boot.fats * fatsz) + root_sect);
		data_sect = total_sect - first_data_sect;
		count_of_clus = data_sect / (boot.sect_per_clus);
		struct fat_info *fat_info;
		fat_info = (struct fat_info *)malloc(sizeof(struct fat_info));
		fat_info->boot = NULL;
		fat_info->boot_fat12 = NULL;
		fat_info->boot_fat16 = NULL;
		fat_info->boot_fat32 = NULL;
		fat_info->fatsz = fatsz;
		fat_info->total_sect = total_sect;
		fat_info->root_sect = root_sect;
		fat_info->first_data_sect = first_data_sect;
		fat_info->data_sect = data_sect;
		if(count_of_clus < 4085) {
			fat_info->type = 12;
			memcpy(fat_info->buf, buf, 512);
			fat_info->boot = (struct boot *)fat_info->buf;
			fat_info->boot_fat12 = (struct boot_fat12 *)(fat_info->buf + 36);
		} else if(count_of_clus < 65525) {
			fat_info->type = 16;
			memcpy(fat_info->buf, buf, 512);
			fat_info->boot = (struct boot *)fat_info->buf;
			fat_info->boot_fat16 = (struct boot_fat16 *)(fat_info->buf + 36);
		} else {
			fat_info->type = 32;
			memcpy(fat_info->buf, buf, 512);
			fat_info->boot = (struct boot *)fat_info->buf;
			fat_info->boot_fat32 = (struct boot_fat32 *)(fat_info->buf + 36);
		}
		printk("FAT%u detected on %s with count_of_clus = %u\n", fat_info->type, dev->name, count_of_clus);
		fat_enum = fs_noparse_new_enumerator(dev, fatfs_enum_vol, "fat_enum");
		fat_enum->class = fat_info;
		return 0;
	} else {
		printk("FAT not detected on %s\n", dev->name);
	}
	return ENODEV;
}

int fatfs_storage_device_notify(struct node_fs *this, struct node_fs *dev)
{
	if(fatfs_storage_volume_notify(dev, dev)) //Check if there is a file system without partition table (floppy)
		fs_noparse_new_notifier(dev, fatfs_storage_volume_notify, "fatfs_vol_notify");
	return 0;
}


int fatfs_driver_read(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	return 0;
}


/*This function checks for already present volumes for file systems.*/
void check_volumes(struct node_fs *dev)
{
	struct node_fs *t;
	t = dev;
	fs_iterator_get_child(&t);
	while(t) {
		fatfs_storage_volume_notify(t,t);
		fs_iterator_get_next(&t);
	}
}


//Todo: Check for already present volumes and dispatch detection for them
int fatfs_driver_write(struct node_fs *this, void *buf, u32 offset, u32 count)
{
	struct node_fs *t;
	switch(offset) {
		case DRIVER_START:
			fs_new_notifier("/class/storage", fatfs_storage_device_notify, "fatfs_dev_notify");
			fs_iterator_parse(&t, "/class/storage");
			if(t) {
				fs_iterator_get_child(&t);
				while(t) {
					if(fatfs_storage_volume_notify(t, t)) { //Check if fs detected.
						//fs not detected. Wait for volumes to appear if any.
						fs_noparse_new_notifier(t, fatfs_storage_volume_notify, "fatfs_vol_notify");
						check_volumes(t);
						fs_iterator_get_next(&t);
					} else
						fs_iterator_get_next(&t);
				}
			}
			return 0;
			
		default: return ENOSUP;
	}
	return 0;
}


struct node_fs fatfs_driver = {
	.type = TYPE_FS_BOTH,
	.name = "fatfs_driver",
	NODE_FS_INIT_VARS,
	.read = fatfs_driver_read,
	.write = fatfs_driver_write,
	.enumerate = dummy,
	.notify = dummy,
	NODE_FS_INIT_INIT,
	NODE_FS_LIST_INIT,
};


int register_fatfs()
{
	fs_link("/driver", &fatfs_driver);
	return 0;
}
